from fastapi import FastAPI, UploadFile, File, Form, HTTPException, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
import pandas as pd
import os
import uuid
import json
import httpx
import time
import asyncio
from typing import List, Optional
from pydantic import BaseModel
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import markdown
from io import BytesIO

app = FastAPI(title="天津交付效能评估平台")

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 创建必要的目录
os.makedirs("uploads", exist_ok=True)
os.makedirs("reports", exist_ok=True)

# 模型API配置
API_KEY = "sk-cgeodmtqtwhrrcaqppopyblkvfwcggowlqryvqwyhoivfiub"
API_URL = "https://api.siliconflow.cn/v1/chat/completions"
MODEL = "Qwen/Qwen3-8B"



# 数据模型
class PromptRequest(BaseModel):
    prompt: str
    excel_id: Optional[str] = None

class Report(BaseModel):
    id: str
    filename: str
    created_at: str
    status: str
    result: Optional[dict] = None

# 存储报告的内存数据库（实际应用中应使用真实数据库）
reports_db = []

# PDF生成函数
def generate_pdf_report(report_data, output_path):
    """生成PDF报告"""
    try:
        # 创建PDF文档
        doc = SimpleDocTemplate(output_path, pagesize=A4)
        story = []
        
        # 获取样式
        styles = getSampleStyleSheet()
        
        # 创建自定义样式
        title_style = ParagraphStyle(
            'CustomTitle',
            parent=styles['Heading1'],
            fontSize=18,
            spaceAfter=30,
            alignment=1,  # 居中
            fontName='Helvetica-Bold'
        )
        
        heading_style = ParagraphStyle(
            'CustomHeading',
            parent=styles['Heading2'],
            fontSize=14,
            spaceAfter=12,
            spaceBefore=20,
            fontName='Helvetica-Bold'
        )
        
        normal_style = ParagraphStyle(
            'CustomNormal',
            parent=styles['Normal'],
            fontSize=11,
            spaceAfter=12,
            leading=16,
            fontName='Helvetica'
        )
        
        # 添加标题
        title = Paragraph("交付效能分析报告", title_style)
        story.append(title)
        story.append(Spacer(1, 20))
        
        # 添加报告基本信息
        info_heading = Paragraph("报告信息", heading_style)
        story.append(info_heading)
        
        created_at = report_data.get('created_at', '未知')
        has_excel = report_data.get('has_excel_data', False)
        data_source = "Excel数据分析" if has_excel else "文本分析"
        prompt_text = report_data.get('prompt', '无')
        
        info_text = f"""
        <b>生成时间：</b>{created_at}<br/>
        <b>分析类型：</b>{data_source}<br/>
        <b>分析要求：</b>{prompt_text}
        """
        
        info_para = Paragraph(info_text, normal_style)
        story.append(info_para)
        story.append(Spacer(1, 20))
        
        # 添加分析结果
        analysis_heading = Paragraph("分析结果", heading_style)
        story.append(analysis_heading)
        
        # 处理分析内容
        analysis_content = report_data.get('analysis', '无分析结果')
        
        # 简单的文本格式化
        analysis_content = analysis_content.replace('\n\n', '<br/><br/>')
        analysis_content = analysis_content.replace('\n', '<br/>')
        analysis_content = analysis_content.replace('**', '<b>').replace('**', '</b>')
        
        # 分段处理长文本
        paragraphs = analysis_content.split('<br/><br/>')
        for para_text in paragraphs:
            if para_text.strip():
                # 清理HTML标签，确保文本安全
                clean_text = para_text.strip()
                para = Paragraph(clean_text, normal_style)
                story.append(para)
                story.append(Spacer(1, 8))
        
        # 添加页脚信息
        story.append(Spacer(1, 30))
        footer_style = ParagraphStyle(
            'Footer',
            parent=styles['Normal'],
            fontSize=9,
            alignment=1,  # 居中
            fontName='Helvetica'
        )
        
        footer_text = "本报告由天津交付效能评估平台自动生成"
        footer = Paragraph(footer_text, footer_style)
        story.append(footer)
        
        # 构建PDF
        doc.build(story)
        print(f"PDF报告生成成功: {output_path}")
        return True
        
    except Exception as e:
        print(f"PDF生成失败: {str(e)}")
        import traceback
        traceback.print_exc()
        
        # 如果PDF生成失败，创建一个简单的文本文件作为备选
        try:
            txt_path = output_path.replace('.pdf', '.txt')
            with open(txt_path, 'w', encoding='utf-8') as f:
                f.write("交付效能分析报告\n")
                f.write("=" * 30 + "\n\n")
                f.write(f"生成时间: {report_data.get('created_at', '未知')}\n")
                f.write(f"分析类型: {'Excel数据分析' if report_data.get('has_excel_data', False) else '文本分析'}\n")
                f.write(f"分析要求: {report_data.get('prompt', '无')}\n\n")
                f.write("分析结果:\n")
                f.write("-" * 20 + "\n")
                f.write(report_data.get('analysis', '无分析结果'))
                f.write("\n\n本报告由天津交付效能评估平台自动生成")
            print(f"备选文本文件已生成: {txt_path}")
        except Exception as txt_error:
            print(f"备选文本文件生成也失败: {str(txt_error)}")
        
        return False

# 上传Excel文件
@app.post("/api/upload")
async def upload_excel(file: UploadFile = File(...)):
    if not file.filename.endswith(('.xls', '.xlsx')):
        raise HTTPException(status_code=400, detail="只接受Excel文件格式(.xls, .xlsx)")
    
    # 生成唯一文件名
    file_id = str(uuid.uuid4())
    file_extension = os.path.splitext(file.filename)[1]
    file_path = f"uploads/{file_id}{file_extension}"
    
    # 保存文件
    with open(file_path, "wb") as f:
        f.write(await file.read())
    
    # 尝试读取Excel验证格式
    try:
        df = pd.read_excel(file_path)
        # 返回基本信息
        return {
            "success": True,
            "file_id": file_id,
            "filename": file.filename,
            "rows": len(df),
            "columns": len(df.columns),
            "column_names": df.columns.tolist()
        }
    except Exception as e:
        # 删除无效文件
        if os.path.exists(file_path):
            os.remove(file_path)
        raise HTTPException(status_code=400, detail=f"Excel文件格式错误: {str(e)}")

# 处理Prompt并调用大模型
@app.post("/api/analyze")
async def analyze_data(request: PromptRequest, background_tasks: BackgroundTasks):
    report_id = str(uuid.uuid4())
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
    
    print(f"创建新报告: {report_id}")
    
    # 创建报告记录
    report = {
        "id": report_id,
        "created_at": timestamp,
        "status": "processing",
        "prompt": request.prompt,
        "filename": "",
        "error": None,
        "result": None
    }
    
    # 如果提供了Excel ID，获取相关数据
    excel_data = None
    if request.excel_id:
        print(f"处理Excel文件ID: {request.excel_id}")
        # 查找上传的Excel文件
        excel_files = [f for f in os.listdir("uploads") if f.startswith(request.excel_id)]
        if not excel_files:
            print(f"未找到Excel文件: {request.excel_id}")
            raise HTTPException(status_code=404, detail="找不到指定的Excel文件")
        
        excel_path = os.path.join("uploads", excel_files[0])
        try:
            df = pd.read_excel(excel_path)
            excel_data = df.to_dict(orient="records")
            print(f"成功读取Excel数据，行数: {len(excel_data)}")
            # 记录文件名
            original_filename = excel_files[0].replace(request.excel_id, "").lstrip(".")
            report["filename"] = f"Excel分析_{original_filename}"
        except Exception as e:
            print(f"读取Excel文件失败: {str(e)}")
            raise HTTPException(status_code=400, detail=f"读取Excel文件失败: {str(e)}")
    else:
        report["filename"] = f"Prompt分析_{timestamp}"
    
    # 添加到报告数据库
    reports_db.append(report)
    print(f"报告已添加到数据库，当前报告总数: {len(reports_db)}")
    
    # 后台任务处理大模型调用
    background_tasks.add_task(process_with_model, report_id, request.prompt, excel_data)
    
    return {"report_id": report_id, "status": "processing"}

# 后台处理函数
async def process_with_model(report_id, prompt, excel_data=None):
    print(f"开始处理报告: {report_id}")
    
    # 查找报告记录
    report = None
    for r in reports_db:
        if r["id"] == report_id:
            report = r
            break
    
    if not report:
        print(f"找不到报告记录: {report_id}")
        return
    
    try:
        # 准备发送给模型的消息
        messages = [
            {"role": "system", "content": "你是一个专业的交付效能分析助手，擅长分析项目数据并提供洞察。请用中文回答，提供详细的分析和建议。"}
        ]
        
        if excel_data:
            # 限制Excel数据大小，避免请求过大
            if len(excel_data) > 50:
                excel_data = excel_data[:50]  # 只取前50行
            
            # 简化数据结构，只保留关键信息
            simplified_data = []
            for row in excel_data:
                # 只保留非空值
                simplified_row = {k: v for k, v in row.items() if v is not None and str(v).strip() != ''}
                if simplified_row:
                    simplified_data.append(simplified_row)
            
            if simplified_data:
                data_str = json.dumps(simplified_data[:20], ensure_ascii=False, indent=2)  # 进一步限制到20行
                user_content = f"请分析以下项目数据:\n{data_str}\n\n分析要求: {prompt}"
            else:
                user_content = f"没有有效的Excel数据，请基于以下要求进行一般性分析: {prompt}"
        else:
            user_content = prompt
        
        messages.append({"role": "user", "content": user_content})
        
        print(f"准备调用API，消息总长度: {len(str(messages))}")
        
        # 重试机制 - 增加重试次数
        max_retries = 5
        for attempt in range(max_retries):
            try:
                print(f"第 {attempt + 1} 次尝试调用API")
                
                # 调用大模型API，增加超时时间到10分钟
                async with httpx.AsyncClient(timeout=600.0) as client:
                    response = await client.post(
                        API_URL,
                        headers={
                            "Authorization": f"Bearer {API_KEY}",
                            "Content-Type": "application/json"
                        },
                        json={
                            "model": MODEL,
                            "messages": messages,
                            "temperature": 0.7,
                            "max_tokens": 1500,
                            "stream": False
                        }
                    )
                    
                    print(f"API响应状态码: {response.status_code}")
                    
                    if response.status_code == 200:
                        result = response.json()
                        
                        # 检查响应格式
                        if "choices" in result and len(result["choices"]) > 0:
                            model_response = result["choices"][0]["message"]["content"]
                            print(f"获得模型响应，长度: {len(model_response)}")
                            
                            # 清理响应内容，移除多余的换行符
                            model_response = model_response.strip()
                            
                            # 生成JSON报告文件（用于前端显示）
                            json_report_path = f"reports/{report_id}.json"
                            try:
                                with open(json_report_path, "w", encoding="utf-8") as f:
                                    json.dump({
                                        "analysis": model_response,
                                        "prompt": prompt,
                                        "created_at": report["created_at"],
                                        "has_excel_data": excel_data is not None
                                    }, f, ensure_ascii=False, indent=2)
                                print(f"JSON报告文件已保存: {json_report_path}")
                            except Exception as file_error:
                                print(f"保存JSON报告文件失败: {str(file_error)}")
                            
                            # 生成PDF报告文件
                            pdf_report_path = f"reports/{report_id}.pdf"
                            report_data = {
                                "analysis": model_response,
                                "prompt": prompt,
                                "created_at": report["created_at"],
                                "has_excel_data": excel_data is not None
                            }
                            
                            if generate_pdf_report(report_data, pdf_report_path):
                                print(f"PDF报告文件已生成: {pdf_report_path}")
                            else:
                                print(f"PDF报告文件生成失败")
                            
                            # 更新报告状态
                            report["status"] = "completed"
                            report["result"] = {"analysis": model_response}
                            print(f"报告 {report_id} 处理完成")
                            return
                        else:
                            print(f"API响应格式错误: {result}")
                            error_msg = "API响应格式错误，无法获取分析结果"
                            report["status"] = "failed"
                            report["error"] = error_msg
                            return
                    
                    elif response.status_code == 401:
                        error_msg = "API密钥无效或已过期，请检查API_KEY配置"
                        print(f"API认证错误: {error_msg}")
                        print(f"使用的API密钥: {API_KEY[:20]}...")
                        report["status"] = "failed"
                        report["error"] = error_msg
                        return
                    
                    elif response.status_code == 429:
                        error_msg = "API调用频率限制，请稍后重试"
                        print(f"API频率限制: {error_msg}")
                        if attempt < max_retries - 1:
                            print(f"等待5秒后重试...")
                            await asyncio.sleep(5)  # 等待5秒后重试
                            continue
                        else:
                            report["status"] = "failed"
                            report["error"] = error_msg
                            return
                    
                    elif response.status_code == 400:
                        error_text = response.text
                        print(f"API请求参数错误: {response.status_code} - {error_text}")
                        error_msg = f"请求参数错误: {error_text}"
                        report["status"] = "failed"
                        report["error"] = error_msg
                        return
                    
                    else:
                        error_text = response.text
                        print(f"API错误: {response.status_code} - {error_text}")
                        if attempt < max_retries - 1:
                            print(f"等待2秒后重试...")
                            await asyncio.sleep(2)  # 等待2秒后重试
                            continue
                        else:
                            error_msg = f"API调用失败 (状态码: {response.status_code}): {error_text}"
                            report["status"] = "failed"
                            report["error"] = error_msg
                            return
                            
            except httpx.TimeoutException:
                print(f"API调用超时 (尝试 {attempt + 1}/{max_retries})")
                if attempt < max_retries - 1:
                    await asyncio.sleep(3)
                    continue
                else:
                    error_msg = "API调用超时，请检查网络连接"
                    report["status"] = "failed"
                    report["error"] = error_msg
                    
                    # 即使失败也生成一个包含错误信息的PDF报告
                    pdf_report_path = f"reports/{report_id}.pdf"
                    error_report_data = {
                        "analysis": f"分析失败：{error_msg}\n\n原始请求：{prompt}\n\n建议：请检查网络连接后重试，或联系系统管理员。",
                        "prompt": prompt,
                        "created_at": report["created_at"],
                        "has_excel_data": excel_data is not None
                    }
                    generate_pdf_report(error_report_data, pdf_report_path)
                    return
                    
            except httpx.RequestError as e:
                print(f"网络请求错误: {str(e)} (尝试 {attempt + 1}/{max_retries})")
                if attempt < max_retries - 1:
                    await asyncio.sleep(3)
                    continue
                else:
                    error_msg = f"网络连接错误: {str(e)}"
                    report["status"] = "failed"
                    report["error"] = error_msg
                    
                    # 生成包含错误信息的PDF报告
                    pdf_report_path = f"reports/{report_id}.pdf"
                    error_report_data = {
                        "analysis": f"分析失败：{error_msg}\n\n原始请求：{prompt}\n\n建议：请检查网络连接后重试，或联系系统管理员。",
                        "prompt": prompt,
                        "created_at": report["created_at"],
                        "has_excel_data": excel_data is not None
                    }
                    generate_pdf_report(error_report_data, pdf_report_path)
                    return
                    
    except Exception as e:
        # 处理其他异常
        print(f"处理异常: {str(e)}")
        import traceback
        traceback.print_exc()
        error_msg = f"处理失败: {str(e)}"
        report["status"] = "failed"
        report["error"] = error_msg
        
        # 生成包含错误信息的PDF报告
        pdf_report_path = f"reports/{report_id}.pdf"
        error_report_data = {
            "analysis": f"分析失败：{error_msg}\n\n原始请求：{prompt}\n\n建议：请联系系统管理员检查系统配置。",
            "prompt": prompt,
            "created_at": report["created_at"],
            "has_excel_data": excel_data is not None
        }
        generate_pdf_report(error_report_data, pdf_report_path)

# 获取报告列表
@app.get("/api/reports")
async def get_reports():
    print(f"当前报告数量: {len(reports_db)}")
    for report in reports_db:
        print(f"报告ID: {report['id']}, 状态: {report['status']}")
    return reports_db

# 获取特定报告
@app.get("/api/reports/{report_id}")
async def get_report(report_id: str):
    print(f"查找报告ID: {report_id}")
    print(f"当前数据库中的报告:")
    for i, report in enumerate(reports_db):
        print(f"  {i}: ID={report['id']}, 状态={report['status']}")
        if "error" in report and report["error"]:
            print(f"     错误信息: {report['error']}")
        if report["id"] == report_id:
            print(f"找到匹配的报告: {report}")
            return report
    print(f"未找到报告ID: {report_id}")
    raise HTTPException(status_code=404, detail=f"报告不存在: {report_id}")

# API健康检查
@app.get("/api/health")
async def health_check():
    # 测试API连接
    api_status = "unknown"
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.post(
                API_URL,
                headers={
                    "Authorization": f"Bearer {API_KEY}",
                    "Content-Type": "application/json"
                },
                json={
                    "model": MODEL,
                    "messages": [{"role": "user", "content": "测试"}],
                    "max_tokens": 10
                }
            )
            if response.status_code == 200:
                api_status = "ok"
            else:
                api_status = f"error_{response.status_code}"
    except Exception as e:
        api_status = f"error: {str(e)}"
    
    return {
        "status": "ok",
        "api_url": API_URL,
        "model": MODEL,
        "api_status": api_status,
        "reports_count": len(reports_db),
        "reports": [{"id": r["id"], "status": r["status"], "error": r.get("error")} for r in reports_db]
    }



# 下载报告
@app.get("/api/reports/{report_id}/download")
async def download_report(report_id: str):
    pdf_report_path = f"reports/{report_id}.pdf"
    if not os.path.exists(pdf_report_path):
        raise HTTPException(status_code=404, detail="PDF报告文件不存在")
    
    # 获取报告名称
    report_name = "report.pdf"
    for report in reports_db:
        if report["id"] == report_id:
            filename_base = report['filename'].replace('.pdf', '').replace('.json', '')
            report_name = f"{filename_base}.pdf"
            break
    
    return FileResponse(
        path=pdf_report_path,
        filename=report_name,
        media_type="application/pdf"
    )

# 挂载静态文件
# 获取项目根目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FRONTEND_DIR = os.path.join(BASE_DIR, "frontend")

app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8888)